iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
自我挑戰組

在30天利用HTML & CSS & JavaScript完成Side Project實作系列 第 9

Day 9 Side Project : Form Wave 表單波浪字體 (下)

  • 分享至 

  • xImage
  •  

這篇延續上篇的波浪字體繼續進行講解

昨天說到javascript的部分可以從兩個部份來講解

  1. 把原本在<label>中的每個字母分開獨立包在<span>
  2. 在最上面的完成圖中,你會看到字母是一個接著一個呈現高低起伏的效果,如同波浪一樣,下一個字母會比上一個字母延遲一下再上升

以上兩點若轉換為程式碼,大概是如下這樣,不過這單純只是示意用喔~

  <!-- Before -->
  <label>Email</label>
  
   <!-- After -->
   <label>
    <span style="transition-delay: 0ms">E</span>
    <span style="transition-delay: 50ms">m</span>
    <span style="transition-delay: 100ms">a</span>
    <span style="transition-delay: 150ms">i</span>
    <span style="transition-delay: 200ms">l</span>
   </label>

實際上的程式碼是這樣

let labels = document.querySelectorAll(".form-control label");
console.log(labels); //NodeList(2) [label, label](類陣列)
labels.forEach((label) => {
  label.innerHTML = label.innerText
    .split("")
    .map(
      (letter, index) =>
        `<span style="transition-delay:${index * 50}ms">${letter}</span>`
    )
    .join("");
});

innerHTML v.s innerText

HTMLElement.innerHTML returns all text, including html tags, that is contained by an element.

HTMLElement.innerText returns all text contained by an element and all its child elements.

innerHTML會選取到的是整個HTML tag,只要是在標籤內的所有內容,像是文字、子標籤都會被選到
innerText會選取標籤和子標籤內的文字部分

上面我把labels這個變數印出來會得到NodeList(2) [label, label],再進一步展開來會看到
https://ithelp.ithome.com.tw/upload/images/20220915/201493624nmZ1ojaKy.png
https://ithelp.ithome.com.tw/upload/images/20220915/20149362tYp6H5fNDZ.png

疑?哪安餒,明明就看起來一樣阿!!
不過還是有一點差別的,只是因為這個例子蒙蔽了我們的雙眼,下面這個例子就可以看出差異了

<div id="test">
  This is the test for<b>innerHTML and innerText</b>:
  <ul>
    <li><a href="#"><b>Hello World</b></a></li>
    <li><a href="#">Welcome To Be A<b>Front-end Engineer</b></a></li>
  </ul>
</div>
let a = document.getElementById('test').innerText
console.log(a)

let b = document.getElementById('test').innerHTML
console.log(b)

印出來的結果如下,想看完整範例檔點此
https://ithelp.ithome.com.tw/upload/images/20220916/20149362ZzSWWraCT8.png

至於賽project中寫label.innerHTML = label.innerText...(後面省略)
是因為我們要選取每個<label>標籤,並把標籤內的內容重新賦值為
<span style="transition-delay: 0ms">E</span>


String.prototype.split()

擷取MDN上的解釋

The split() method takes a pattern and divides a String into an ordered list of substrings by searching for the pattern, puts these substrings into an array, and returns the array.

split()是字串處理方法,它可以將原字串依照指定的分割符(separator)拆分成子字串,並放入陣列中回傳

語法

https://ithelp.ithome.com.tw/upload/images/20220916/20149362vcZKZx4zKo.png

  • (選填)參數 separator 用來指定分割符
  • (選填)參數 limit 是非必要的,表示最多返回幾個子字串
  • split() 結果會回傳一個字串陣列
 console.log(label.innerText);  //Email Password

出於好奇心,想看看所有的東西內部到底是啥,所以把label.innerText印出來,會得到Email、Password,資料型別是string,那我們要把它拆分成E、m、a、i、l並放入一個陣列中,最好用的當然就是split()啦~它可以把原字串依照指定的符號分割成子字串,並放入陣列中回傳

 console.log(label.innerText.split(""));  //['E', 'm', 'a', 'i', 'l']  ['P', 'a', 's', 's', 'w', 'o', 'r', 'd']

Array.prototype.map()

擷取MDN上的解釋

The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.

意思上大致是說map()會創建一個新的array並回傳,我們可以用map()來迭代陣列內的每個元素

語法

https://ithelp.ithome.com.tw/upload/images/20220915/20149362d8X81c7AxA.png

那我把我們賽project的部分程式碼複製下來看,不然一直捲捲很麻煩

label.innerText
    .split("")
    .map(
      (letter, index) =>
        `<span style="transition-delay:${idx * 50}ms">${letter}</span>`
    )

我們利用map(letter, idx)=>{...省略...}來迭代陣列中的元素讓它去做我們想讓它做的事,在這裡又發揮了我雞婆的精神(?),把兩個參數印出來看看XD

console.log(letter, index);

打開開發者工具,呈現如下
https://ithelp.ithome.com.tw/upload/images/20220915/20149362A3QcJHwdk6.png
https://ithelp.ithome.com.tw/upload/images/20220915/20149362gNZSUqt1iY.png
回想一下一開始所說的,字母是一個接著一個呈現高低起伏的效果,如同波浪一樣,下一個字母會比上一個字母延遲一下再上升 這句話一直在我腦海中迴盪~

我得到了每個字母,那我不是可以把每個字母放在<span>中嗎?
我得到了每個字母的順序,那我不是可以依照它的先後順序來控制延遲時間transition-delay嗎?

看到這裡開始覺得熱血沸騰了?好像搞懂為甚麼要這樣寫了?
所以<span style="transition-delay:${idx * 50}ms">${letter}</span>
就是 <span style="transition-delay: 0ms">E</span> (比較直覺式的呈現XD)
記得要加上ms

[注意]map()常被拿來跟forEach()做比較,這裡可以參考這篇文章


Array.prototype.join()

擷取MDN上的解釋

The join() method creates and returns a new string by concatenating all of the elements in an array (or an array-like object), separated by commas or a specified separator string.

意思大致上是說map()會將陣列或一個類陣列集合(array-like collection)中所有的元素連接、合併成一個字串(根據join()中的給的參數),並回傳。

語法

https://ithelp.ithome.com.tw/upload/images/20220915/20149362XXY6d9yX0Z.png

我們的程式碼我再把它貼下來來一次

let labels = document.querySelectorAll(".form-control label");
console.log(labels); //NodeList(2) [label, label](類陣列)
labels.forEach((label) => {
  label.innerHTML = label.innerText
    .split("")
    .map(
      (letter, index) =>
        `<span style="transition-delay:${index * 50}ms">${letter}</span>`
    )
    .join("");
});

上面map()會回傳的是一個新的陣列,但是我們要的是"字串"而不是陣列,所以這時候join()就登場了!! 原本['E','m','a','i','l']因為.join("")的加入,變成"Email",字串的形式


為了配合javaScript新增加的CSS

.form-control label span {
  display: inline-block;
  font-size: 18px;
  min-width: 5px;
  transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.form-control input:focus + label span,
.form-control input:valid + label span {
  color: lightblue;
  transform: translateY(-30px);
}

字母的波浪在:focus跟:valid的狀態時會向上移動,所以transform: translateY(-30px)代表字母會往y軸的方向往上移動30px,並且為了讓動畫更平滑更自然一點,加上transition的timing-function為貝茲曲線(Cubic Bezier)

cubic-bezier(x1,y1,x2,y2)是一個連續曲線,總共有四個點,分別是第一個貝茲曲線控制點x1和y1以及第二個貝茲曲線控制點x2和y2、此外,x1、x2的範圍為0-1,y1,y2的範圍就不侷限,可以是負數,藉由控制Cubic Bezier,我們可以更容易的做出加減速的效果

附上codepen連結https://codepen.io/hangineer/pen/NWMbNZL


那今天的部分就先到這啦
若有解說不夠詳盡或是錯誤歡迎指教,感激不盡!那明天見囉/images/emoticon/emoticon29.gif


參考資料

Difference Between textContents, innerText, and innerHtml
MDN-Array.prototype.map()
非常彈性好用的陣列 Array 方法 map()
CSS3 Cubic Bezier


上一篇
Day 8 Side Project : Form Wave 表單波浪字體 (上)
下一篇
Day 10 Side Project : Sound Board 聲音操控版
系列文
在30天利用HTML & CSS & JavaScript完成Side Project實作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言